package com.xuecheng.orders.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.base.utils.IdWorkerUtils;
import com.xuecheng.base.utils.QRCodeUtil;
import com.xuecheng.messagesdk.model.po.MqMessage;
import com.xuecheng.messagesdk.service.MqMessageService;
import com.xuecheng.orders.config.AlipayConfig;
import com.xuecheng.orders.config.PayNotifyConfig;
import com.xuecheng.orders.mapper.XcOrdersGoodsMapper;
import com.xuecheng.orders.mapper.XcOrdersMapper;
import com.xuecheng.orders.mapper.XcPayRecordMapper;
import com.xuecheng.orders.model.dto.AddOrderDto;
import com.xuecheng.orders.model.dto.PayRecordDto;
import com.xuecheng.orders.model.dto.PayStatusDto;
import com.xuecheng.orders.model.po.XcOrders;
import com.xuecheng.orders.model.po.XcOrdersGoods;
import com.xuecheng.orders.model.po.XcPayRecord;
import com.xuecheng.orders.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 订单相关的接口实现
 */
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Value("${pay.alipay.APP_ID}")
    String APP_ID;
    @Value("${pay.alipay.APP_PRIVATE_KEY}")
    String APP_PRIVATE_KEY;

    @Value("${pay.alipay.ALIPAY_PUBLIC_KEY}")
    String ALIPAY_PUBLIC_KEY;

    @Autowired
    XcOrdersMapper ordersMapper;

    @Autowired
    XcOrdersGoodsMapper ordersGoodsMapper;

    @Autowired
    XcPayRecordMapper payRecordMapper;

    @Autowired
    OrderServiceImpl currentProxy;

    @Autowired
    MqMessageService mqMessageService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Value("${pay.qrcodeurl}")
    String qrcodeurl;

    @Transactional
    @Override
    public PayRecordDto createOrder(String userId, AddOrderDto addOrderDto) throws IOException {
        // 进行幂等性判断，同一个选课记录只能有一个订单
        // 调用service，完成插入订单信息，订单明细，
        XcOrders xcOrders = saveXcOrders(userId, addOrderDto);
        // 插入支付记录，
        XcPayRecord xcPayRecord = creatPayRecord(xcOrders);
        // 生成支付二维码
        Long payNo = xcPayRecord.getPayNo();
        QRCodeUtil qrCodeUtil = new QRCodeUtil();
        String url = String.format(qrcodeurl, payNo);
        String qrCode = null;
        try{
            qrCode = qrCodeUtil.createQRCode(
                    "url?payno="+payNo,200,200);
        }catch (IOException e){
            XueChengPlusException.cast("生成二维码出错");
        }
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(xcPayRecord, payRecordDto);
        payRecordDto.setQrcode(qrCode);
        return payRecordDto;
    }

    /**
     * 保存订单信息
     * @param userId
     * @param addOrderDto
     * @return
     */
    public XcOrders saveXcOrders(String userId, AddOrderDto addOrderDto){
        //幂等性处理
        //进行幂等性判断，同一个选课记录只能有一个订单；
        XcOrders xcOrders = getOrderByBusinessId(addOrderDto.getOutBusinessId());
        if(xcOrders!=null){
            return xcOrders;
        }
        //插入订单表，订单主页
        xcOrders = new XcOrders();
        xcOrders.setId(IdWorkerUtils.getInstance().nextId());
        xcOrders.setTotalPrice(addOrderDto.getTotalPrice());
        xcOrders.setCreateDate(LocalDateTime.now());
        xcOrders.setStatus("600001");
        xcOrders.setUserId(userId);
        xcOrders.setOrderType("60201");
        xcOrders.setOrderName(addOrderDto.getOrderName());
        xcOrders.setOrderDescrip(addOrderDto.getOrderDescrip());
        xcOrders.setOrderDetail(addOrderDto.getOrderDetail());
        xcOrders.setOutBusinessId(addOrderDto.getOutBusinessId());//如果是选课，这里记录了选课表的id
        int insert = ordersMapper.insert(xcOrders);
        if(insert<=0){
            XueChengPlusException.cast("添加订单失败");
        }
        Long orderId = xcOrders.getId();
        // 订单明细表，将前端传入的明细json串转成list
        String orderDetailJson = addOrderDto.getOrderDetail();
        List<XcOrdersGoods> xcOrdersGoods = JSON.parseArray(orderDetailJson, XcOrdersGoods.class);
        // 遍历 xcOrdersGoods，插入订单明细
        xcOrdersGoods.forEach(goods->{
            goods.setOrderId(orderId);
            int insert1 = ordersGoodsMapper.insert(goods);
        });
        return xcOrders;
    }

    /**
     * 创建支付记录
     * @param orders
     * @return
     */
    public XcPayRecord creatPayRecord(XcOrders orders){
        //如果此订单不存在，不能添加支付记录
        Long orderId = orders.getId();
        XcOrders xcOrders = ordersMapper.selectById(orderId);
        if(xcOrders == null){
            XueChengPlusException.cast("订单不存在");
        }
        //如果此订单支付结果为成功，不再添加支付记录，避免重复支付
        String status = xcOrders.getStatus();
        if("601002".equals(status)){
            XueChengPlusException.cast("此订单已支付");
        }
        //添加支付记录
        XcPayRecord xcPayRecord = new XcPayRecord();
        // 如果此订单支付结果为成功，不再添加支付记录，避免重复支付
        xcPayRecord.setPayNo(IdWorkerUtils.getInstance().nextId());//支付记录号，将来要传给支付宝
        xcPayRecord.setOrderId(orderId);
        xcPayRecord.setOrderName(xcOrders.getOrderName());
        xcPayRecord.setTotalPrice(xcOrders.getTotalPrice());
        xcPayRecord.setCurrency("CNY");
        xcPayRecord.setCreateDate(LocalDateTime.now());
        xcPayRecord.setStatus("601001");
        xcPayRecord.setUserId(xcOrders.getUserId());
        int insert = payRecordMapper.insert(xcPayRecord);
        if(insert<=0){
            XueChengPlusException.cast("插入支付记录失败");
        }
        return xcPayRecord;
    }

    /**
     * 根据业务id查订单
     * @param businssId
     * @return
     */
    public XcOrders getOrderByBusinessId(String businssId){
        XcOrders orders = ordersMapper.selectOne(
                new LambdaQueryWrapper<XcOrders>()
                        .eq(XcOrders::getOutBusinessId,businssId)
        );
        return orders;
    }

    /**
     * 查询支付记录号
     * @param payNo
     * @return
     */
    @Override
    public XcPayRecord getPayRecordByPayno(String payNo){
        XcPayRecord xcPayRecord = payRecordMapper.selectOne(
                new LambdaQueryWrapper<XcPayRecord>()
                        .eq(XcPayRecord::getPayNo, payNo));
        return  xcPayRecord;
    }

    /**
     * 请求支付宝查询支付结果
     * @param payNo
     * @return
     */
    @Override
    public PayRecordDto queryPayResult(String payNo){

        //调用支付宝的接口查询支付结果
        PayStatusDto payStatusDto = queryPayResultFromAlipay(payNo);
        //拿到支付结果更新支付记录表和订单表的支付状态
        currentProxy.saveAliPayStatus(payStatusDto);
        XcPayRecord payRecord = getPayRecordByPayno(payNo);
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecord, payRecordDto);
        return payRecordDto;
    }

    /**
     * 请求支付宝查询支付的结果
     * @param payNo
     * @return
     */
    public PayStatusDto queryPayResultFromAlipay(String payNo) {

        AlipayClient alipayClient = new DefaultAlipayClient(
                AlipayConfig.URL,
                APP_ID,
                APP_PRIVATE_KEY,
                "json",
                AlipayConfig.CHARSET,
                ALIPAY_PUBLIC_KEY,
                AlipayConfig.SIGNTYPE); //获得初始化的AlipayClient
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", payNo);
        //bizContent.put("trade_no", "2014112611001004680073956707");
        request.setBizContent(bizContent.toString());
        String body = null;
        try {
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if(!response.isSuccess()){//交易不成功
                XueChengPlusException.cast("请求支付宝查询支付结果失败");
            }

            body = response.getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
            XueChengPlusException.cast("请求支付查询支付结果异常");
        }
        Map<String, String> bodyMap = JSON.parseObject(body, Map.class);
        PayStatusDto payStatusDto = new PayStatusDto();
        payStatusDto.setOut_trade_no(payNo);
        payStatusDto.setTrade_no(bodyMap.get("trade_no"));//支付宝的交易号
        payStatusDto.setTrade_status(bodyMap.get("trade_status"));//交易状态
        payStatusDto.setApp_id(APP_ID);
        payStatusDto.setTotal_amount(bodyMap.get("total_amount"));//总金额
        return payStatusDto;

    }

    /**
     * 保存支付宝支付结果
     * @param payStatusDto
     */
    @Transactional
    @Override
    public void saveAliPayStatus(PayStatusDto payStatusDto){

        //支付记录号
        String payNo = payStatusDto.getTrade_no();
        //如果支付成功
        XcPayRecord payRecordByPayno = getPayRecordByPayno(payNo);
        if(payRecordByPayno == null){
            XueChengPlusException.cast("找不到相关的支付记录");
        }
        Long orderId = payRecordByPayno.getOrderId();
        XcOrders xcOrders = ordersMapper.selectById(orderId);
        if(xcOrders == null){
            XueChengPlusException.cast("找不到相关联的订单");
        }
        //拿到支付状态
        String status = payRecordByPayno.getStatus();
        if("601002".equals(status)){
            //如果已经成功
            return;
        }
        String trade_status = payStatusDto.getTrade_status();
        //更新支付记录表的状态为支付成功
        if(trade_status.equals("TRADE_SUCCESS")){
            payRecordByPayno.setStatus("601002");
            payRecordByPayno.setOutPayNo(payStatusDto.getTrade_no());
            payRecordByPayno.setOutPayChannel("Alipay");
            payRecordByPayno.setPaySuccessTime(LocalDateTime.now());
            payRecordMapper.updateById(payRecordByPayno);
        }
        //更新订单表的状态为支付成功
        xcOrders.setStatus("600002");
        ordersMapper.updateById(xcOrders);

        //将消息写到数据库
        MqMessage mqMessage = mqMessageService.addMessage("payresult_notify",
                xcOrders.getOutBusinessId(),
                xcOrders.getOrderType(),
                null
                );
        //发送消息
        notifyPayResult(mqMessage);
    }

    /**
     * 发送通知结果
     * @param mqMessage
     */
    public void notifyPayResult(MqMessage mqMessage){
        // 消息内容
        String jsonString = JSON.toJSONString(mqMessage);
        // 创建一个持久化消息
        Message messageObj = MessageBuilder.withBody(jsonString.getBytes(StandardCharsets.UTF_8)).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
        //消息id
        Long id = mqMessage.getId();
        // 全局消息id
        CorrelationData correlationData = new CorrelationData(id.toString());
        correlationData.getFuture().addCallback(result->{
          if(result.isAck()){
              // 消息成功发送到了交换机
              log.debug("发送消息成功:{}",jsonString);
              // 将消息从数据库表mq_message 中删除
              mqMessageService.completed(id);
          }else{
              // 消息发送失败
              log.debug("发送消息失败:{}",jsonString);
          }
        },ex->{
          // 发生异常了
            log.debug("发送消息异常:{}",jsonString);
        });
        rabbitTemplate.convertAndSend(PayNotifyConfig.PAYNOTIFY_EXCHANGE_FANOUT, "", messageObj, correlationData);
    }

}
